package com.chenlehao.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect // AOP类
@Slf4j
@Order(1)
// 记录方法执行的时间
public class timeAspect {
    // 公用切入点
    @Pointcut("execution(* com.chenlehao.service.*.*(..))")
    private void pt() {
    }

    // AOP的作用是面向方法的编程, 可以在方法前和方法后写逻辑
    // 环绕通知，此注解标注的通知方法在目标方法前、后都被执行
    // @Around环绕通知需要自己调用joinPoint.proceed()
    // @Around环绕通知方法的返回值, 必须指定为Object, 来接收原始方法的返回值
    @Around("pt()") // 切入点
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 调用方法前执行逻辑
        // 1. 记录开始时间
        long begin = System.currentTimeMillis();

        // 调用方法
        // 2. 调用原始方法运行
        Object result = joinPoint.proceed();

        // 调用方法后执行逻辑
        // 3. 记录结束时间, 计算方法执行耗时
        long end = System.currentTimeMillis();
        log.info(joinPoint.getSignature() + "方法执行耗时: {}ms", end - begin);
        return result;
    }

    // 前置通知，此注解标注的通知方法在目标方法前被执行
    @Before("pt()")
    public void before() {
        log.info("前置通知");
    }

    // 后置通知，此注解标注的通知方法在目标方法后被执行，无论是否有异常都会执行
    @After("pt()")
    public void after() {
        log.info("后置通知");
    }

    // 返回后通知，此注解标注的通知方法在目标方法后被执行，有异常不会执行
    @AfterReturning("pt()")
    public void afterReturning() {
        log.info("返回后通知");
    }

    // 异常后通知，此注解标注的通知方法发生异常后执行
    @AfterThrowing("pt()")
    public void afterThrowing() {
        log.info("异常后通知");
    }
}
